import {
  getSidebar,
  DefaultSidebar,
  NoteDictsUtils,
} from "@dendronhq/common-all";
import { runEngineTestV5 } from "../../engine";
import { ENGINE_HOOKS } from "../../presets";

/*
 * See [[dendron://dendron.dendron-site/dendron.topic.publish.sidebar#complex-sidebar-example]] for sidebar docs and example
 */

describe("GIVEN sidebar config input", () => {
  describe("WHEN providing empty config", () => {
    test("THEN return empty sidebar object", async () => {
      await runEngineTestV5(
        async ({ engine }) => {
          const engineNotes = await engine.findNotes({ excludeStub: false });
          const sidebarResp = getSidebar([], {
            notes: NoteDictsUtils.createNotePropsByIdDict(engineNotes),
          });
          expect(sidebarResp._unsafeUnwrap()).toHaveLength(0);
        },
        {
          expect,
          preSetupHook: ENGINE_HOOKS.setupBasic,
        }
      );
    });
  });

  describe("WHEN providing DefaultSidebar", () => {
    test("THEN return sidebar object", async () => {
      await runEngineTestV5(
        async ({ engine }) => {
          const engineNotes = await engine.findNotes({ excludeStub: false });
          const sidebarResp = getSidebar(DefaultSidebar, {
            notes: NoteDictsUtils.createNotePropsByIdDict(engineNotes),
          });
          expect(sidebarResp._unsafeUnwrap()).toMatchSnapshot();
          // Expect DefaultSidebar to resolve into 2 sidebarItem entries
          expect(sidebarResp._unsafeUnwrap()).toHaveLength(2);
          // Expect `foo.md` to have the child `foo.ch1.md`
          expect(sidebarResp._unsafeUnwrap()).toHaveProperty(
            "[1].items[0].id",
            "foo.ch1"
          );
        },
        {
          expect,
          preSetupHook: ENGINE_HOOKS.setupBasic,
        }
      );
    });
  });

  describe("WHEN providing autogenerated sidebar item", () => {
    test("THEN return sidebar slice", async () => {
      await runEngineTestV5(
        async ({ engine }) => {
          const engineNotes = await engine.findNotes({ excludeStub: false });
          const sidebarResp = getSidebar(
            [{ type: "autogenerated", id: "foo" }],
            {
              notes: NoteDictsUtils.createNotePropsByIdDict(engineNotes),
            }
          );
          expect(sidebarResp._unsafeUnwrap()).toMatchSnapshot();
          // Expect to resolve into sidebar slice with two children
          expect(sidebarResp._unsafeUnwrap()).toHaveLength(2);
          // Expect to contain deep nested note
          expect(sidebarResp._unsafeUnwrap()).toHaveProperty(
            "[0].items[0].items[0].id",
            "foo.ch1.gch1.ggch1"
          );
        },
        {
          expect,
          preSetupHook: ENGINE_HOOKS.setupHierarchyForLookupTests,
        }
      );
    });
  });

  describe("WHEN providing custom sidebar", () => {
    test("THEN return sidebar object", async () => {
      await runEngineTestV5(
        async ({ engine }) => {
          const engineNotes = await engine.findNotes({ excludeStub: false });
          const sidebarResp = getSidebar(
            [
              {
                type: "note",
                label: "Foo label",
                id: "foo",
              },
              {
                type: "category",
                label: "Some category label",
                link: { type: "note", id: "bar" },
                items: [
                  {
                    type: "autogenerated",
                    id: "foo.ch1",
                  },
                  {
                    type: "note",
                    label: "Some deep note",
                    id: "goo.ends-with-ch1.no-ch1-by-itself",
                  },
                ],
              },
            ],
            { notes: NoteDictsUtils.createNotePropsByIdDict(engineNotes) }
          );
          expect(sidebarResp._unsafeUnwrap()).toMatchSnapshot();
          expect(sidebarResp._unsafeUnwrap()).toHaveProperty(
            "[1].items[0].items[0].id",
            "foo.ch1.gch1.ggch1"
          );
          expect(sidebarResp._unsafeUnwrap()).toHaveProperty(
            "[1].items[2].id",
            "goo.ends-with-ch1.no-ch1-by-itself"
          );
        },
        {
          expect,
          preSetupHook: ENGINE_HOOKS.setupHierarchyForLookupTests,
        }
      );
    });
  });

  describe("WHEN nav_exclude_children is enabled", () => {
    test("THEN return sidebar object", async () => {
      await runEngineTestV5(
        async ({ engine }) => {
          const domain = (await engine.getNote("foo")).data!;
          domain.custom.nav_exclude_children = true;
          await engine.writeNote(domain, { metaOnly: true });
          const engineNotes = await engine.findNotes({ excludeStub: false });
          const sidebarResp = getSidebar(
            [{ type: "autogenerated", id: "root" }],
            {
              notes: NoteDictsUtils.createNotePropsByIdDict(engineNotes),
            }
          );
          expect(sidebarResp._unsafeUnwrap()).toMatchSnapshot();
          // Expect foo note to have no children
          const fooSidebarItem = sidebarResp._unsafeUnwrap()?.[1];
          expect(fooSidebarItem).toHaveProperty("items", []);
        },
        {
          expect,
          preSetupHook: ENGINE_HOOKS.setupBasic,
        }
      );
    });
  });

  describe("WHEN has_collection is enabled", () => {
    test("THEN return sidebar object", async () => {
      await runEngineTestV5(
        async ({ engine }) => {
          const domain = (await engine.getNote("foo")).data!;
          domain.custom.has_collection = true;
          await engine.writeNote(domain, { metaOnly: true });
          const engineNotes = await engine.findNotes({ excludeStub: false });
          const sidebarResp = getSidebar(
            [{ type: "autogenerated", id: "root" }],
            {
              notes: NoteDictsUtils.createNotePropsByIdDict(engineNotes),
            }
          );
          expect(sidebarResp._unsafeUnwrap()).toMatchSnapshot();
          // Expect foo note to have no children
          const fooSidebarItem = sidebarResp._unsafeUnwrap()?.[1];
          expect(fooSidebarItem).toHaveProperty("items", []);
        },
        {
          expect,
          preSetupHook: ENGINE_HOOKS.setupBasic,
        }
      );
    });

    describe("AND nav_exclude_children is set to false", () => {
      test("THEN return sidebar object", async () => {
        await runEngineTestV5(
          async ({ engine }) => {
            const domain = (await engine.getNote("foo")).data!;
            domain.custom.has_collection = true;
            domain.custom.nav_exclude_children = false;
            await engine.writeNote(domain, { metaOnly: true });
            const engineNotes = await engine.findNotes({ excludeStub: false });
            const sidebarResp = getSidebar(
              [{ type: "autogenerated", id: "root" }],
              {
                notes: NoteDictsUtils.createNotePropsByIdDict(engineNotes),
              }
            );
            expect(sidebarResp._unsafeUnwrap()).toMatchSnapshot();
            // Expect foo note to have no children
            const fooSidebarItem = sidebarResp._unsafeUnwrap()?.[1];
            expect(fooSidebarItem).toHaveProperty("items", []);
          },
          {
            expect,
            preSetupHook: ENGINE_HOOKS.setupBasic,
          }
        );
      });
    });
  });

  describe("WHEN nav_exclude is enabled", () => {
    test("THEN return sidebar object", async () => {
      await runEngineTestV5(
        async ({ engine }) => {
          const domain = (await engine.getNote("foo")).data!;
          domain.custom.nav_exclude = true;
          await engine.writeNote(domain, { metaOnly: true });
          const engineNotes = await engine.findNotes({ excludeStub: false });
          const sidebarResp = getSidebar(
            [{ type: "autogenerated", id: "root" }],
            {
              notes: NoteDictsUtils.createNotePropsByIdDict(engineNotes),
            }
          );
          expect(sidebarResp._unsafeUnwrap()).toMatchSnapshot();
          // Expect `foo.md` to be not part of the sidebar
          expect(sidebarResp._unsafeUnwrap()).toHaveLength(1);
        },
        {
          expect,
          preSetupHook: ENGINE_HOOKS.setupBasic,
        }
      );
    });
  });
});
